package com.bblocks.common.anti_reptile.rule;

import com.bblocks.common.anti_reptile.constant.RuleResultEnum;
import com.bblocks.common.anti_reptile.module.MatchResult;
import com.bblocks.common.anti_reptile.service.NoticeService;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 规则执行入口
 * @Author Zhao [125043150@qq.com]
 * @Date 2024/3/24 11:39
 * @Version 1.0
 * @since 8
 */
@Slf4j
public class RuleActuator {

    private final List<AntiReptileRule> ruleList;
    private final List<NoticeService> noticeServices;

    public RuleActuator(List<AntiReptileRule> rules,List<NoticeService> noticeServices) {
        this.ruleList = rules;
        this.noticeServices = noticeServices;
    }

    /**
     * 是否允许通过请求，并
     * @param request 请求
     * @param response 响应
     * @return 请求是否允许通过(存在则不通过)
     */
    public MatchResult isAllowed(HttpServletRequest request , HttpServletResponse response){
        for (AntiReptileRule rule: ruleList){
            MatchResult matchResult = rule.match(request,response);
            if (matchResult != null){
                toMatchNotices(matchResult);

                if(RuleResultEnum.RATELIMIT_ALARM.getLockType().equals(matchResult.getMatchRule().getLockType())){
                    matchResult = null;
                }

                return matchResult;
            }
        }
        return null;
    }

    /**
     * 重置验证码黑名单（解除全部-config配置的黑名单除外）
     * 目前有2种黑名单类型：
     * 1、验证码黑名单-时间大概10分钟，resetTypeLimit={@link RuleResultEnum.RATELIMIT_CAPTCHA}，来解锁（业务端验证码通过来解锁-用户发起）
     * 2、黑名单-时间大概1天，resetTypeLimit={@link RuleResultEnum.RATELIMIT_BLACK}，来解锁（一般是控制台解锁黑名单-控制台发起）
     *
     * @param request
     * @param realRequestUri
     * @param resetTypeLimit 解锁黑名单类型（为空则全部类型都解除）
     * @return 返回重置的黑名单（可能多个）
     */
    public List<MatchResult> reset(HttpServletRequest request, String realRequestUri, RuleResultEnum resetTypeLimit){
        List<MatchResult> results = ruleList.stream()
                                            .map(rule->rule.reset(request, realRequestUri,resetTypeLimit))
                                            .filter(Objects::nonNull)
                                            .collect(Collectors.toList());
        if(!results.isEmpty()){
            results.forEach(matchResult -> {
                toBlackOverNotices(matchResult);
            });
        }
        return results;
    }


    private void toMatchNotices(MatchResult matchResult){
        noticeServices.forEach(notice->{
            try {
                notice.onLimiterEvent(matchResult);
            }catch (Exception e){
                log.warn("onLimiterNotice err:",e);
            }
        });
    }

    private void toBlackOverNotices(MatchResult matchResult){
        noticeServices.forEach(notice->{
            try {
                notice.onBlackOverEvent(matchResult);
            }catch (Exception e){
                log.warn("onBlackOverNotice err:",e);
            }
        });
    }


}
